package graph;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

public class Graph {

    public static void main(String[] args) {
        int n = 5;
        String[] vertices = { "A", "B", "C", "D", "E" };
        Graph graph = new Graph(n);
        for (String v : vertices) {
            graph.insertVertex(v);
        }
        graph.addEdge(0, 1, 1);
        graph.addEdge(0, 2, 1);
        graph.addEdge(1, 2, 1);
        graph.addEdge(1, 3, 1);
        graph.addEdge(1, 4, 1);
        graph.showGraph();
        // System.out.println("深度优先遍历");
        // graph.dfs();
        // System.out.println();
        System.out.println("广度优先遍历");
        graph.bfs();
    }

    private ArrayList<String> vertexList;
    private int[][] edges;
    private int numOfEdges;
    private boolean[] isVisited; // 记录节点是否被访问过

    Graph(int n) {
        this.edges = new int[n][n];
        this.vertexList = new ArrayList<String>(n);
        numOfEdges = 0;
        isVisited = new boolean[n];
    }

    /* 深度遍历相关代码 */

    // 对一个节点使用dfs
    private void dfs(boolean[] isVisited, int i) {
        System.out.print(getValueByIndex(i) + "->");
        isVisited[i] = true;
        int w = getFirstNeightbor(i);
        while (w != -1) {
            if (!isVisited[w]) {
                dfs(isVisited, w);
            }
            w = getNextNeighbor(i, w);
        }
    }

    // 重载dfs方法，对每一个节点使用dfs
    public void dfs() {
        for (int i = 0; i < countVertices(); i++) {
            if (!isVisited[i]) {
                dfs(isVisited, i);
            }
        }
    }

    // 获取一节点第一个邻接节点的下标
    public int getFirstNeightbor(int v) {
        for (int j = 0; j < vertexList.size(); j++) {
            if (edges[v][j] != 0) {
                return j;
            }
        }
        return -1;
    }

    // 获取一节点v1从v2之后下一个邻接节点的下标
    public int getNextNeighbor(int v1, int v2) {
        for (int j = v2 + 1; j < vertexList.size(); j++) {
            if (edges[v1][j] != 0) {
                return j;
            }
        }
        return -1;
    }

    /* 深度遍历相关代码 */

    // 对一个节点使用bfs
    private void bfs(boolean[] isVisited, int i) {
        int u; // 队列头结点的下标
        int w; // 邻接节点
        LinkedList<Integer> queue = new LinkedList<Integer>(); // 队列，记录节点访问顺序
        System.out.print(getValueByIndex(i) + "->");
        isVisited[i] = true;
        queue.addLast(i);
        while (!queue.isEmpty()) {
            // 取出队列头结点下标
            u = queue.removeFirst();
            // 得到第一个邻接节点的下标
            w = getFirstNeightbor(u);
            while (w != -1) {
                if (!isVisited[w]) {
                    System.out.print(getValueByIndex(w) + "->");
                    isVisited[w] = true;
                    queue.addLast(w);
                }
                // 以u为前驱节点，找w之后下一个邻接节点的下标
                w = getNextNeighbor(u, w);

            }
        }
    }

    // 重载bfs
    public void bfs() {
        for (int i = 0; i < countVertices(); i++) {
            if (!isVisited[i]) {
                bfs(isVisited, i);
            }
        }
    }

    /* 构造代码 */

    // 插入节点
    public void insertVertex(String vertex) {
        this.vertexList.add(vertex);
    }

    // 插入边
    public void addEdge(int v1, int v2, int weight) {
        this.edges[v1][v2] = weight;
        this.edges[v2][v1] = weight;
        numOfEdges++;
    }

    public int countVertices() {
        return vertexList.size();
    }

    public int countEdges() {
        return numOfEdges;
    }

    public String getValueByIndex(int i) {
        return vertexList.get(i);
    }

    public int getWeight(int v1, int v2) {
        return edges[v1][v2];
    }

    // 输出图的邻接矩阵
    public void showGraph() {
        for (int[] link : edges) {
            System.out.println(Arrays.toString(link));
        }
    }

}
